use crate::ast::*;
use crate::error::{CompilerError, Result};
use inkwell::types::BasicType;
use inkwell::values::BasicValueEnum;
use inkwell::AddressSpace;

use super::super::LLVMBackend;

impl<'ctx> LLVMBackend<'ctx> {
    /// Generate slice range expression (e.g., slice[1:3], slice[:5], slice[2:], slice[:])
    /// Returns a new slice struct with pointer, length, and capacity
    pub(super) fn generate_slice_range_expression(
        &mut self,
        slice_range: &SliceRangeExpr,
    ) -> Result<BasicValueEnum<'ctx>> {
        // Get the source slice
        let source_value = self.generate_expression(&slice_range.object)?;

        // Get the type of the source object
        let source_type = if let Expression::Variable(var_name) = &*slice_range.object {
            self.variable_type_map.get(var_name).cloned()
        } else {
            None
        };

        // Only slices can be sliced
        let vector_type = match &source_type {
            Some(Type::Vec(st)) => st.clone(),
            Some(other_type) => {
                return Err(CompilerError::internal(&format!(
                    "Slice range operation only supported on slices, got: {:?}",
                    other_type
                )));
            }
            None => {
                // If type not found in map, provide more helpful error
                let var_name = if let Expression::Variable(name) = &*slice_range.object {
                    name.clone()
                } else {
                    "<expression>".to_string()
                };
                return Err(CompilerError::internal(&format!(
                    "Cannot determine type of '{}' for slice range operation. Variable not found in type map.",
                    var_name
                )));
            }
        };

        // Get the element type
        let element_llvm_type =
            self.nrc_type_to_llvm_type(&Some(*vector_type.element_type.clone()))?;

        // Build the slice struct type
        let slice_struct_type = self.context.struct_type(
            &[
                element_llvm_type.ptr_type(AddressSpace::default()).into(),
                self.context.i64_type().into(),
                self.context.i64_type().into(),
            ],
            false,
        );

        // Extract fields from the source slice
        let source_slice_struct = if source_value.is_struct_value() {
            source_value.into_struct_value()
        } else {
            return Err(CompilerError::internal(
                "Source value is not a slice struct",
            ));
        };

        // Extract ptr, len, cap from source slice
        let source_ptr = self
            .builder
            .build_extract_value(source_slice_struct, 0, "source_ptr")?
            .into_pointer_value();

        let source_len = self
            .builder
            .build_extract_value(source_slice_struct, 1, "source_len")?
            .into_int_value();

        let _source_cap = self
            .builder
            .build_extract_value(source_slice_struct, 2, "source_cap")?
            .into_int_value();

        // Calculate start and end indices
        let zero = self.context.i64_type().const_int(0, false);

        let start_index = if let Some(ref start_expr) = slice_range.start {
            let start_value = self.generate_expression(start_expr)?;
            if start_value.is_int_value() {
                let start_int = start_value.into_int_value();
                // Ensure it's i64 (cast if necessary)
                if start_int.get_type().get_bit_width() != 64 {
                    self.builder
                        .build_int_cast(start_int, self.context.i64_type(), "start_cast")?
                } else {
                    start_int
                }
            } else {
                return Err(CompilerError::internal("Start index must be an integer"));
            }
        } else {
            // No start index means start from beginning
            zero
        };

        let end_index = if let Some(ref end_expr) = slice_range.end {
            let end_value = self.generate_expression(end_expr)?;
            if end_value.is_int_value() {
                let end_int = end_value.into_int_value();
                // Ensure it's i64 (cast if necessary)
                if end_int.get_type().get_bit_width() != 64 {
                    self.builder
                        .build_int_cast(end_int, self.context.i64_type(), "end_cast")?
                } else {
                    end_int
                }
            } else {
                return Err(CompilerError::internal("End index must be an integer"));
            }
        } else {
            // No end index means go to the end
            source_len
        };

        // Bounds checking (optional, for safety)
        // TODO: Add runtime bounds checking
        // For now, we trust the indices are valid

        // Calculate new length: end_index - start_index
        let new_len = self
            .builder
            .build_int_sub(end_index, start_index, "slice_range_len")?;

        // Get element size for memory operations
        let element_size = match &*vector_type.element_type {
            Type::Basic(basic_type) => {
                use crate::ast::types::BasicType;
                match basic_type {
                    BasicType::Int8 | BasicType::Uint8 | BasicType::Char => 1,
                    BasicType::Int16 | BasicType::Uint16 => 2,
                    BasicType::Int32 | BasicType::Uint32 | BasicType::Float32 => 4,
                    BasicType::Int
                    | BasicType::Int64
                    | BasicType::Uint
                    | BasicType::Uint64
                    | BasicType::Float64 => 8,
                    BasicType::Bool => 1,
                    _ => 8,
                }
            }
            _ => 8,
        };

        // Allocate new memory for the range (deep copy)
        let element_size_const = self.context.i64_type().const_int(element_size, false);
        let alloc_size = self
            .builder
            .build_int_mul(new_len, element_size_const, "alloc_size")?;

        let malloc_func = self
            .function_map
            .get("__malloc")
            .ok_or_else(|| CompilerError::internal("__malloc function not found"))?;
        let malloc_call =
            self.builder
                .build_call(*malloc_func, &[alloc_size.into()], "range_malloc")?;
        let new_ptr_i8 = malloc_call
            .try_as_basic_value()
            .left()
            .ok_or_else(|| CompilerError::internal("malloc call did not return a value"))?
            .into_pointer_value();

        let new_ptr = self
            .builder
            .build_bitcast(
                new_ptr_i8,
                element_llvm_type.ptr_type(AddressSpace::default()),
                "new_ptr_typed",
            )?
            .into_pointer_value();

        // Calculate source pointer for the range: source_ptr + start_index
        let range_source_ptr = unsafe {
            self.builder.build_gep(
                element_llvm_type,
                source_ptr,
                &[start_index],
                "range_source_ptr",
            )?
        };

        // Copy the range data using memcpy
        let copy_size = self
            .builder
            .build_int_mul(new_len, element_size_const, "copy_size")?;

        let memcpy_intrinsic = self
            .module
            .get_function("llvm.memcpy.p0.p0.i64")
            .unwrap_or_else(|| {
                let i8_ptr_type = self.context.i8_type().ptr_type(AddressSpace::default());
                let memcpy_type = self.context.void_type().fn_type(
                    &[
                        i8_ptr_type.into(),
                        i8_ptr_type.into(),
                        self.context.i64_type().into(),
                        self.context.bool_type().into(),
                    ],
                    false,
                );
                self.module
                    .add_function("llvm.memcpy.p0.p0.i64", memcpy_type, None)
            });

        let dest_i8 = self
            .builder
            .build_bitcast(
                new_ptr,
                self.context.i8_type().ptr_type(AddressSpace::default()),
                "dest_i8",
            )?
            .into_pointer_value();

        let src_i8 = self
            .builder
            .build_bitcast(
                range_source_ptr,
                self.context.i8_type().ptr_type(AddressSpace::default()),
                "src_i8",
            )?
            .into_pointer_value();

        self.builder.build_call(
            memcpy_intrinsic,
            &[
                dest_i8.into(),
                src_i8.into(),
                copy_size.into(),
                self.context.bool_type().const_int(0, false).into(),
            ],
            "memcpy_call",
        )?;

        // Build the new slice struct with the copied data
        let mut new_slice = slice_struct_type.get_undef();
        new_slice = self
            .builder
            .build_insert_value(new_slice, new_ptr, 0, "insert_ptr")?
            .into_struct_value();

        new_slice = self
            .builder
            .build_insert_value(new_slice, new_len, 1, "insert_len")?
            .into_struct_value();

        // For capacity, use the new length (tight capacity)
        new_slice = self
            .builder
            .build_insert_value(new_slice, new_len, 2, "insert_cap")?
            .into_struct_value();

        Ok(new_slice.into())
    }
}
